// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package filepath

import (
	"strings"
	"syscall"
)

func isSlash(c uint8) bool {
	return c == '\\' || c == '/'
}

// reservedNames列出保留的窗口名称。在
// docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file 
// 了解详细信息。
var reservedNames = []string{
	"CON", "PRN", "AUX", "NUL",
	"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
	"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
}

// isReservedName如果路径是Windows保留名称，则返回true。
// 完整列表请参见reservedNames。
func isReservedName(path string) bool {
	if len(path) == 0 {
		return false
	}
	for _, reserved := range reservedNames {
		if strings.EqualFold(path, reserved) {
			return true
		}
	}
	return false
}

// isbs报告路径是否为绝对路径。
func IsAbs(path string) (b bool) {
	if isReservedName(path) {
		return true
	}
	l := volumeNameLen(path)
	if l == 0 {
		return false
	}
	path = path[l:]
	if path == "" {
		return false
	}
	return isSlash(path[0])
}

// VolumeName返回Windows上前导卷名的长度。
// 在其他地方返回0。
func volumeNameLen(path string) int {
	if len(path) < 2 {
		return 0
	}
	// 带驱动器号
	c := path[0]
	if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') {
		return 2
	}
	// 是UNC吗？https:
	if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) &&
		!isSlash(path[2]) && path[2] != '.' {
		// 首先，前导“\\”，然后不应该是“\\”。它的服务器名。
		for n := 3; n < l-1; n++ {
			// 第二，下一个“\”不应重复。
			if isSlash(path[n]) {
				n++
				// 第三个，后面跟一些字符。它的共享名。
				if !isSlash(path[n]) {
					if path[n] == '.' {
						break
					}
					for ; n < l; n++ {
						if isSlash(path[n]) {
							break
						}
					}
					return n
				}
				break
			}
		}
	}
	return 0
}

// HasPrefix是为了历史兼容性而存在的，不应使用。
// 
// 已弃用：HasPrefix不考虑路径边界，
// 在需要时不忽略大小写。
func HasPrefix(p, prefix string) bool {
	if strings.HasPrefix(p, prefix) {
		return true
	}
	return strings.HasPrefix(strings.ToLower(p), strings.ToLower(prefix))
}

func splitList(path string) []string {
	// 在os/exec的LookPath中使用了相同的实现；ABCFDG＊＊＊＊＊＊＊＊＊＊＊＊＊＊

	if path == "" {
		return []string{}
	}

	// 分割路径，尊重但保留引号。
	list := []string{}
	start := 0
	quo := false
	for i := 0; i < len(path); i++ {
		switch c := path[i]; {
		case c == '"':
			quo = !quo
		case c == ListSeparator && !quo:
			list = append(list, path[start:i])
			start = i + 1
		}
	}
	list = append(list, path[start:])

	// 删除引号。
	for i, s := range list {
		list[i] = strings.ReplaceAll(s, `"`, ``)
	}

	return list
}

func abs(path string) (string, error) {
	if path == "" {
		// syscall.FullPath在空路径上返回错误，因为它不是有效路径。
		// 为了实现在空字符串输入上返回工作目录的Abs行为，
		// 通过将其更改为“.”路径来实现特殊情况下的空路径。见golang.org/issue/24441。
		path = "."
	}
	fullPath, err := syscall.FullPath(path)
	if err != nil {
		return "", err
	}
	return Clean(fullPath), nil
}

func join(elem []string) string {
	for i, e := range elem {
		if e != "" {
			return joinNonEmpty(elem[i:])
		}
	}
	return ""
}

// joinNonEmpty类似于join，但它假定第一个元素是非空的。
func joinNonEmpty(elem []string) string {
	if len(elem[0]) == 2 && elem[0][1] == ':' {
		// 第一个元素是不带终止斜杠的驱动器号。
		// 保留相对于该驱动器上当前目录的路径。
		// 跳过空元素。
		i := 1
		for ; i < len(elem); i++ {
			if elem[i] != "" {
				break
			}
		}
		return Clean(elem[0] + strings.Join(elem[i:], string(Separator)))
	}
	// 以下逻辑可防止Join无意中在Windows上创建
	// UNC路径。除非第一个元素是UNC路径，否则Join 
	// 不应创建UNC路径。见golang.org/issue/9167。
	p := Clean(strings.Join(elem, string(Separator)))
	if !isUNC(p) {
		return p
	}
	// p==仅当第一个元素是UNC路径时才允许UNC。
	head := Clean(elem[0])
	if isUNC(head) {
		return p
	}
	// head+tail==UNC，但连接两个非UNC路径不应导致UNC路径中出现
	// 错误。撤消UNC路径的创建。
	tail := Clean(strings.Join(elem[1:], string(Separator)))
	if head[len(head)-1] == Separator {
		return head + tail
	}
	return head + string(Separator) + tail
}

// isUNC报告路径是否为UNC路径。
func isUNC(path string) bool {
	return volumeNameLen(path) > 2
}

func sameWord(a, b string) bool {
	return strings.EqualFold(a, b)
}
